#include "Queue.h"
#include "../../embeddable/Embeddable.h"

#ifndef NULL
#define NULL 0
#endif

template<typename Data>
Queue<Data>::Queue()
{

	array = NULL;
	size = 0; 
	max_size = 0;
	start = 0;
	end = 0;
	allocated = 1;
	NoResize = 0;
}


template<typename Data>
Queue<Data>::Queue(const Queue<Data>& q) 
{
	allocated = q.allocated;
	if(allocated == 1)
	{
		if(q.array != NULL)
		{
			//allocate a copy of the queue now.
			array = (Data*) Embeddable::embed_malloc(sizeof(Data)*q.max_size);
			unsigned int i = 0;
			for(i = 0; i < q.max_size; i++)
			{
				array[i] = q.array[i];
			}
		}
		else
			array = q.array; //this will be null.
	}
	max_size = q.max_size;
	size = q.size;
	start = q.start;
	end = q.end;
	NoResize = q.NoResize;	
}

template<typename Data>
Queue<Data>& Queue<Data>::operator=(const Queue<Data>& q) /*volatile*/
{
	if(array == q.array)
		return *this;
	else
	{
		if(allocated == 1) //the queue is allocated in heap
		{
			if(array != NULL) //then deallocate.
				Embeddable::embed_free(array);
		}

	allocated = q.allocated;
	if(allocated == 1)
	{
		if(q.array != NULL)
		{
			//allocate a copy of the queue now.
			array = (Data*) Embeddable::embed_malloc(sizeof(Data)*q.max_size);
			unsigned int i = 0;
			for(i = 0; i < q.max_size; i++)
			{
				array[i] = q.array[i];
			}
		}
		else
			array = q.array; //this will be null.
	}
	max_size = q.max_size;
	size = q.size;
	start = q.start;
	end = q.end;
	NoResize = q.NoResize;	
		return *this;
	}
}
template<typename Data>
Queue<Data>::Queue(Data* a, unsigned int s) 
{
	array = a;
	size = 0; 
	max_size = s;
	start = 0;
	end = 0;
	allocated = 0;
	NoResize = 0;
}


template<typename Data>
Queue<Data>::Queue(unsigned char r, unsigned int size)
{
	unsigned int t = sizeof(Data);
	array = (Data*) Embeddable::embed_malloc(t*size);
	size = 0; 
	max_size = size;
	start = 0;
	end = 0;
	allocated = 1;
	NoResize = r;
}


template<typename Data>
void Queue<Data>::resize() /*volatile*/
{
	if(array == NULL) 
	{
		array = (Data*) Embeddable::embed_malloc(10*sizeof(Data));
		size = 0;
		max_size = 10;
	}
	else
	{
		if(size >= max_size)
		{
			Data* array2 = array;
			array = (Data*) Embeddable::embed_malloc(max_size*2*sizeof(Data));
			int i = 0;
			if(start >= end)
			{
				for(i = start; i < max_size; i++)
				{
					array[i-start]=array2[i];
				}
				for(i = 0; i < end; i++)
				{
					array[i+max_size-start] = array2[i];

				}
			}
			else
			{
				for(i = start; i < end; i++)
				{
					array[i-start] = array2[i];
				}
			}
			start = 0;
			end = size;
			max_size = max_size*2;
			Embeddable::embed_free(array2);
		}
	}
	
}


template<typename Data>
void Queue<Data>::push(Data data) /*volatile*/
{
	if(NoResize == 0 && (array == NULL || size >= max_size))
	{
		resize();
	}
	//add the data to the array.
	Data d2 = data;
	array[end] = d2;
	end = (end + 1) % max_size; //end increments by 1 mod with max_size
	size = size + 1;
	return;
}

template<typename Data>
Data Queue<Data>::pop()  /*volatile*/
{
	Data value = array[start];
	start = start + 1;
	if(start == max_size)
		start = 0;
	if(size != 0)
		size = size - 1;
	return value;

}

template<typename Data>
Queue<Data>::~Queue<Data>()
{
	if(array != NULL && allocated == 1)
	{
		//free the array
		Embeddable::embed_free(array);
		array = NULL;
		size = 0;
		max_size = 0;
		start = 0;
		end = 0;
	}
}

template<typename Data>
unsigned char Queue<Data>::full() /*volatile*/
{
	if((array == NULL || size >= max_size) && NoResize == 1)
		return 1;
	else
		return 0;
}

template<typename Data>
unsigned char Queue<Data>::empty() /*volatile*/
{
	if(array == NULL || size == 0)
		return 1;
	else 
		return 0;
}




